package com.gintama.base.model;


import static com.gintama.base.enums.BaseResultCode.ERROR_CODE;
import static com.gintama.base.enums.BaseResultCode.ERROR_MSG;
import static com.gintama.base.enums.BaseResultCode.OK_CODE;
import static com.gintama.base.enums.BaseResultCode.OK_MSG;

import cn.hutool.core.lang.Opt;
import com.gintama.base.enums.BaseResultCode;
import com.gintama.base.enums.ResultCodeSeries;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import lombok.Data;

/**
 * @author zhenyao.zhao
 * @since 2019/5/15
 */
@Data
@ApiModel(description = "结果信息类 - 响应结果封装对象，功能类似 OperationResult，但只有一条处理结果消息，避免过多的信息影响信息处理。")
public class Result<T> implements Serializable {

	private static final long serialVersionUID = 6121750405105567729L;
	@ApiModelProperty(value = "处理结果的状态编码")
	private String code;
	@ApiModelProperty(value = "处理结果的信息")
	public String msg;
	@ApiModelProperty(value = "处理结果的数据")
	public T data;
	@ApiModelProperty(value = "是否成功")
	public boolean ok;
	@ApiModelProperty("附加返回数据")
	private Map<String, Object> extraData;

	public Result() {
		this.extraData = new HashMap<>(0);
	}

	private Result(String code, String msg, T data) {
		if (code == null) {
			throw new IllegalArgumentException("Code can not be null");
		}
		int okOrErrLength = 4, otherErrorLength = 6;
		if (okOrErrLength != code.length()
				&& otherErrorLength != code.length()) {
			throw new IllegalArgumentException(
					"Code is correct, please use enum of ResultCode");
		}
		this.ok = BaseResultCode.isOk(this.code);
		this.code = code;
		this.msg = msg;
		this.data = data;
		this.extraData = new HashMap<>(0);
	}

	public boolean isOk() {
		return BaseResultCode.isOk(this.code);
	}

	public ResultCodeSeries series() {
		return ResultCodeSeries.value(this.code);
	}

	// -------- static ok() --------

	public static <T> Result<T> ok() {
		return Result.ok(OK_MSG, null);
	}

	public static <T> Result<T> ok(final String msg, final T data) {
		return Result.of(OK_CODE, msg, data);
	}

	public static <T> Result<T> ok4msg(final String msg) {
		return Result.ok(msg, null);
	}

	public static <T> Result<T> ok4data(final T data) {
		return Result.ok(OK_MSG, data);
	}

	// -------- static err() --------

	public static <T> Result<T> err() {
		return Result.err(ERROR_CODE, ERROR_MSG, null);
	}
	
	public static <T> Result<T> err(final BaseResultCode resultCode) {
		return err(resultCode, null);
	}

	public static <T> Result<T> err(final BaseResultCode resultCode,
			final T data) {
		String code = Optional.ofNullable(resultCode).map(BaseResultCode::code)
				.orElse(ERROR_CODE);
		String msg = Optional.ofNullable(resultCode).map(BaseResultCode::msg)
				.orElse(ERROR_MSG);
		return Result.err(code, msg, data);
	}

	public static <T> Result<T> err(final String code, final String msg) {
		return Result.err(code, msg, null);
	}

	public static <T> Result<T> err(final String code, final String msg,
			final T data) {
		if (OK_CODE.equalsIgnoreCase(code)) {
			throw new IllegalArgumentException(
					"Can not build error with code " + OK_CODE);
		}
		return Result.of(code, msg, data);
	}

	public static <T> Result<T> err4msg(final String msg) {
		return Result.err(ERROR_CODE, msg, null);
	}

	public static <T> Result<T> err4data(final T data) {
		return Result.err(ERROR_CODE, ERROR_MSG, data);
	}

	// -------- static of() --------

	public static <T> Result<T> of(String code, String msg, T data) {
		return new Result<>(code, msg, data);
	}

	public static <T> Result<T> newInstance(Result oldResult) {
		return Result.<T>newInstance(oldResult, null);
	}

	public static <T> Result<T> newInstance(Result oldResult, T data) {
		Result<T> newResult;
		if (oldResult != null) {
			newResult = new Result<T>(oldResult.getCode(), oldResult.getMsg(), data);
		} else {
			newResult = new Result<T>();
			Opt.ofNullable(data).ifPresent(newResult::setData);
		}
		return newResult;
	}

}
